package hn.sigit.dao.hnd.cadastre;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.hibernatespatial.criterion.SpatialRestrictions;

import com.vividsolutions.jts.geom.Geometry;

import hn.sigit.dao.ladm.spatialunit.LA_SpatialUnitDAO;
import hn.sigit.model.hnd.administrative.HND_RuleOperatorType;
import hn.sigit.model.hnd.cadastre.HND_Layer;
import hn.sigit.model.hnd.cadastre.HND_SpatialZone;

public class HND_SpatialZoneDAO extends LA_SpatialUnitDAO {
	private static final long serialVersionUID = 1L;

	
	public static HND_SpatialZone loadSpatialZoneById(long id) {
		return loadSpatialZoneById(getSession(), id);
	}
	public static List<HND_SpatialZone> loadSpatialZonesByLayer(HND_Layer layer) {
		return loadSpatialZonesByLayer(getSession(), layer);
	}
	public static List<HND_SpatialZone> loadSpatialZonesByNameAndLayer(String zoneName, HND_Layer layer) {
		return loadSpatialZonesByNameAndLayer(getSession(), zoneName, layer);
	}
	public static List<HND_SpatialZone> loadSpatialZonesByGeom(Geometry theGeom, HND_RuleOperatorType rot) {
		return loadSpatialZonesByGeom(getSession(), theGeom, rot);
	}
	public static List<HND_SpatialZone> loadSpatialZonesByLayerAndGeom(HND_Layer layer, Geometry theGeom, HND_RuleOperatorType rot) {
		return loadSpatialZonesByLayerAndGeom(getSession(), layer, theGeom, rot);
	}
	public static Set<HND_SpatialZone> loadNeighborsBySpatialZone(HND_SpatialZone hndSpatialZone) {
		return loadNeighborsBySpatialZone(getSession(), hndSpatialZone);
	}
	
	
	public static HND_SpatialZone loadSpatialZoneById(Session session, long id) {
		Criteria criteria = session.createCriteria(HND_SpatialZone.class);
		criteria.add(Restrictions.eq("suID", id));
		
		return (HND_SpatialZone) criteria.uniqueResult();
	}
	@SuppressWarnings("unchecked")
	public static List<HND_SpatialZone> loadSpatialZonesByLayer(Session session, HND_Layer layer) {
		Criteria criteria = session.createCriteria(HND_SpatialZone.class);
		criteria.add(Restrictions.eq("level", layer));
		
		return criteria.list();
	}
	@SuppressWarnings("unchecked")
	public static List<HND_SpatialZone> loadSpatialZonesByNameAndLayer(Session session, String zoneName, HND_Layer layer) {
		Criteria criteria = session.createCriteria(HND_SpatialZone.class);
		criteria.add(Restrictions.like("zoneName", zoneName, MatchMode.START).ignoreCase());
		criteria.add(Restrictions.eq("level", layer));
		
		return criteria.list();
	}
	
	@SuppressWarnings("unchecked")
	public static List<HND_SpatialZone> loadSpatialZonesByLayerAndGeom(Session session, HND_Layer layer,
			Geometry theGeom, HND_RuleOperatorType rot) {
		Criteria criteria = session.createCriteria(HND_SpatialZone.class);
		criteria.add(Restrictions.eq("level", layer));
		addSpatialRestriction(criteria, theGeom, rot);
		
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	public static List<HND_SpatialZone> loadSpatialZonesByGeom(Session session, Geometry theGeom, HND_RuleOperatorType rot) {
		Criteria criteria = session.createCriteria(HND_SpatialZone.class);
		addSpatialRestriction(criteria, theGeom, rot);
		
		return criteria.list();
	}
	
	private static void addSpatialRestriction(Criteria criteria, Geometry geom, HND_RuleOperatorType rot) {
		switch (rot) {
		case DISTANCE:
			criteria.add(SpatialRestrictions.intersects("shape", geom));
			break;
		case OVERLAP:
			criteria.add(SpatialRestrictions.overlaps("shape", geom));
			break;
		case WITHIN:
			criteria.add(SpatialRestrictions.within("shape", geom));
			break;
		case TOUCH:
			criteria.add(SpatialRestrictions.touches("shape", geom));
			break;
		case CROSS:
			criteria.add(SpatialRestrictions.crosses("shape", geom));
			break;
		case CONTAINS:
			criteria.add(SpatialRestrictions.contains("shape", geom));
			break;
		default:
			throw new IllegalArgumentException(); 
		}
	}

	public static Set<HND_SpatialZone> loadNeighborsBySpatialZone(Session session, HND_SpatialZone hndSpatialZone) {
		Geometry filter = hndSpatialZone.getShape();
		
		if (filter != null) {
			Set<HND_SpatialZone> hndSpatialZoneSet = new HashSet<HND_SpatialZone>();
			
			Criteria criteria = session.createCriteria(HND_SpatialZone.class);
			criteria.add(SpatialRestrictions.touches("shape", filter));
			
			@SuppressWarnings("unchecked")
			List<HND_SpatialZone> hndTouchingSpatialZoneList = (List<HND_SpatialZone>)criteria.list();
			if (hndTouchingSpatialZoneList != null)
				for (HND_SpatialZone hndTouchingSZ : hndTouchingSpatialZoneList)
					//TODO: Parameterize if we want touching parcels with intersection > 0 included or not
					//if (hndTouchingP.getShape().intersection(filter).getLength() > 0.0001)
					hndSpatialZoneSet.add(hndTouchingSZ);
			
			return hndSpatialZoneSet;
		}
		
		return null;
	}
}
